#include "CharFeature.h"
#include "Debug.h"
#include "ConfigFile.h"
#include "Config.h"
#include "Log.h"
#include "Config.h"

#include <cfloat>
#include <cstring>
#include <cmath>
#include <fstream>

CharFeature::CharFeature() {
    ifstream fin(Config::getInstance().getString("path.feature_mm").c_str());
    
    if (!fin) {
        Log::log("File \"feature.mm\" does not exist, and default value will be evalued to max and min array!", Log::INFO);

        for (int i = 0; i < s_FEATURE_SIZE; i++) {
            m_max[i] = FLT_MAX;
            m_min[i] = 0;
        }

        return;
    }

    for (int i = 0; i < s_FEATURE_SIZE; i++) {
        fin >> m_max[i] >> m_min[i];
    }

    fin.close();
}

void CharFeature::extractFeature(float* data, const uchar* imageData, bool updateMaxMin) {
    // no need to update now
    assert(updateMaxMin == false);

    // the feature describes a character image
    int strokeWidth; // stroke width
    int totalStrokeLen; // total stroke length
    int projHist[2][s_NORM_SIZE]; // projection histogram in two directions
    int transitions[2]; // number of transitions in two directions
    int strokeDensity[2][s_STRIPESIZE]; // stroke density in two directions
    int peripheral[4][s_STRIPESIZE][2]; // two peripheral features with four directions
    int locDir[s_GRIDSIZE][s_GRIDSIZE][4]; // local direction contributivity with four regions and four directions
    float strokeProp[2][s_GRIDSIZE][4]; // stroke proportion in two directions
    int maxLocDirCtr[s_GRIDSIZE][s_GRIDSIZE][4]; // maximum local direction contributivity
    int totalBlackJump[2][s_SUBVCOUNT]; // black jump distribution in each balanced subvectors
    float divBlackJump[2][s_SUBVCOUNT]; // black jump distribution in each balanced subvectors divided by the total

    calcStrokeWidthAndLen(imageData, &strokeWidth, &totalStrokeLen);
    calcProjHist(imageData, projHist);
    calcTransDensAndPeri(imageData, transitions, strokeDensity, peripheral);
    calcLocDirPropAndMaxLocDir(imageData, locDir, strokeProp, maxLocDirCtr);
    calcBlackJump(imageData, totalBlackJump, divBlackJump);

    int count = 0, tempInt;
    float tempFloat;

    // int strokeWidth;
    //data[count++] = strokeWidth;	// ignore this feature

    //int totalStrokeLen;   0
    if (updateMaxMin) {
        if (m_max[count] < totalStrokeLen) {
            m_max[count] = totalStrokeLen;
        }
        if (m_min[count] > totalStrokeLen) {
            m_min[count] = totalStrokeLen;
        }
    }
    data[count++] = totalStrokeLen;

    // int projHist[2][s_NORM_SIZE]; 1-128
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < s_NORM_SIZE; j++) {
            tempInt = projHist[i][j];

            if (updateMaxMin) {
                if (m_max[count] < tempInt) {
                    m_max[count] = tempInt;
                }
                if (m_min[count] > tempInt) {
                    m_min[count] = tempInt;
                }
            }
            data[count++] = tempInt;
        }
    }

    // int transitions[2];  129-130
    if (updateMaxMin) {
        if (m_max[count] < transitions[0]) {
            m_max[count] = transitions[0];
        }
        if (m_min[count] > transitions[0]) {
            m_min[count] = transitions[0];
        }
    }
    data[count++] = transitions[0];

    if (updateMaxMin) {
        if (m_max[count] < transitions[1]) {
            m_max[count] = transitions[1];
        }
        if (m_min[count] > transitions[1]) {
            m_min[count] = transitions[1];
        }
    }
    data[count++] = transitions[1];

    // int strokeDensity[2][s_STRIPESIZE];  131-146
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < s_STRIPESIZE; j++) {
            tempInt = strokeDensity[i][j];

            if (updateMaxMin) {
                if (m_max[count] < tempInt) {
                    m_max[count] = tempInt;
                }
                if (m_min[count] > tempInt) {
                    m_min[count] = tempInt;
                }
            }
            data[count++] = tempInt;
        }
    }

    // int peripheral[4][s_STRIPESIZE][2];  147-210
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < s_STRIPESIZE; j++) {
            for (int k = 0; k < 2; k++) {
                tempInt = peripheral[i][j][k];

                if (updateMaxMin) {
                    if (m_max[count] < tempInt) {
                        m_max[count] = tempInt;
                    }
                    if (m_min[count] > tempInt) {
                        m_min[count] = tempInt;
                    }
                }
                data[count++] = tempInt;
            }
        }
    }

    // int locDir[s_GRIDSIZE][s_GRIDSIZE][4];   211-274
    for (int i = 0; i < s_GRIDSIZE; i++) {
        for (int j = 0; j < s_GRIDSIZE; j++) {
            for (int k = 0; k < 4; k++) {
                tempInt = locDir[i][j][k];

                if (updateMaxMin) {
                    if (m_max[count] < tempInt) {
                        m_max[count] = tempInt;
                    }
                    if (m_min[count] > tempInt) {
                        m_min[count] = tempInt;
                    }
                }
                data[count++] = tempInt;
            }
        }
    }

    // float strokeProp[2][s_GRIDSIZE][4];  275-306
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < s_GRIDSIZE; j++) {
            for (int k = 0; k < 4; k++) {
                tempFloat = strokeProp[i][j][k];

                if (updateMaxMin) {
                    if (m_max[count] < tempFloat) {
                        m_max[count] = tempFloat;
                    }
                    if (m_min[count] > tempFloat) {
                        m_min[count] = tempFloat;
                    }
                }
                data[count++] = tempFloat;
            }
        }
    }

    // int maxLocDirCtr[s_GRIDSIZE][s_GRIDSIZE][4];  307-370
    for (int i = 0; i < s_GRIDSIZE; i++) {
        for (int j = 0; j < s_GRIDSIZE; j++) {
            for (int k = 0; k < 4; k++) {
                tempInt = maxLocDirCtr[i][j][k];

                if (updateMaxMin) {
                    if (m_max[count] < tempInt) {
                        m_max[count] = tempInt;
                    }
                    if (m_min[count] > tempInt) {
                        m_min[count] = tempInt;
                    }
                }
                data[count++] = tempInt;
            }
        }
    }

    // int totalBlackJump[2][s_SUBVCOUNT];  371-386
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < s_SUBVCOUNT; j++) {
            tempInt = totalBlackJump[i][j];

            if (updateMaxMin) {
                if (m_max[count] < tempInt) {
                    m_max[count] = tempInt;
                }
                if (m_min[count] > tempInt) {
                    m_min[count] = tempInt;
                }
            }
            data[count++] = tempInt;
        }
    }

    // float divBlackJump[2][s_SUBVCOUNT];  387-402
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < s_SUBVCOUNT; j++) {
            tempFloat = divBlackJump[i][j];

            if (updateMaxMin) {
                if (m_max[count] < tempFloat) {
                    m_max[count] = tempFloat;
                }
                if (m_min[count] > tempFloat) {
                    m_min[count] = tempFloat;
                }
            }
            data[count++] = tempFloat;
        }
    }

    assert(count == s_FEATURE_SIZE);
}

void CharFeature::extractScaledFeature(float* data, const uchar* imageData) {
    extractFeature(data, imageData);
    scaleFeature(data);
}

void CharFeature::scaleFeature(float* feature) {
    for (int i = 0; i < s_FEATURE_SIZE; i++) {
        feature[i] = (feature[i] - m_min[i]) / (m_max[i] - m_min[i]);
    }
}

void CharFeature::calcStrokeWidthAndLen(const uchar* imageData, int* strokeWidth, int* totalStrokeLen) {
    int charPixCount, hit;

    charPixCount = hit = 0;

    for (int i = 0; i < s_NORM_SIZE - 1; i++) {
        for (int j = 0; j < s_NORM_SIZE - 1; j++) {
            if (*(imageData + s_NORM_SIZE * i + j) == CHAR) {
                charPixCount++;

                if (*(imageData + s_NORM_SIZE * (i + 1) + j) == CHAR) {
                    if (j > 0 && *(imageData + s_NORM_SIZE * (i + 1) + j - 1) == CHAR) {
                        if (*(imageData + s_NORM_SIZE * i + j + 1) == CHAR) {
                            hit++;
                        }
                    } else if (*(imageData + s_NORM_SIZE * i + j + 1) == CHAR && *(imageData + s_NORM_SIZE * (i + 1) + j + 1) == CHAR) {
                        hit++;
                    }
                } else if (j < s_NORM_SIZE - 2 && *(imageData + s_NORM_SIZE * i + j + 1) == CHAR && *(imageData + s_NORM_SIZE * (i + 1) + j + 1) == CHAR && *(imageData + s_NORM_SIZE * (i + 1) + j + 2) == CHAR) {
                    hit++;
                }
            } else if (*(imageData + s_NORM_SIZE * i + j) != BG) { // TODO
                std::cout << "FeatureExtracter::calcStrokeWidthAndLen:\n\t*(imageData + size*i + j) == " << (int) *(imageData + s_NORM_SIZE * i + j) << std::endl;
            }
        }
    }

    for (int i = 0; i < s_NORM_SIZE - 1; i++) {
        if (*(imageData + s_NORM_SIZE * i + s_NORM_SIZE - 1) == CHAR) {
            charPixCount++;
        }

        if (*(imageData + s_NORM_SIZE * (s_NORM_SIZE - 1) + i) == CHAR) {
            charPixCount++;
        }
    }

    if (*(imageData + s_NORM_SIZE * s_NORM_SIZE - 1) == CHAR) {
        charPixCount++;
    }


    *strokeWidth = (hit == 0) ? 1 : round(charPixCount * 1.0f / (charPixCount - hit));
    *totalStrokeLen = round(charPixCount * 1.0f / *strokeWidth);
}

void CharFeature::calcProjHist(const uchar* imageData, int projHist[][s_NORM_SIZE]) {
    memset(projHist, 0, 2 * s_NORM_SIZE * sizeof (int));

    for (int i = 0; i < s_NORM_SIZE; i++) {
        for (int j = 0; j < s_NORM_SIZE; j++) {
            if (*(imageData + s_NORM_SIZE * i + j) == CHAR) {
                projHist[HORIZONTAL][j]++;
                projHist[VERTICAL][i]++;
            }
        }
    }
}

void CharFeature::calcTransDensAndPeri(const uchar* imageData, int* transitions, int strokeDensity[][s_STRIPESIZE], int peripheral[][s_STRIPESIZE][2]) {
    bool isFirst[4][s_NORM_SIZE];

    enum Order {
        FIRST = 0, SECOND
    };

    transitions[HORIZONTAL] = transitions[VERTICAL] = 0;

    memset(strokeDensity, 0, 2 * s_STRIPESIZE * sizeof (int));

    for (int i = 0; i < s_STRIPESIZE; i++) {
        peripheral[EAST][i][FIRST] = peripheral[SOUTH][i][FIRST] = peripheral[WEST][i][FIRST] = peripheral[NORTH][i][FIRST] = s_NORM_SIZE;
        peripheral[EAST][i][SECOND] = peripheral[SOUTH][i][SECOND] = peripheral[WEST][i][SECOND] = peripheral[NORTH][i][SECOND] = s_NORM_SIZE;
    }

    for (int i = 0; i < s_NORM_SIZE; i++) {
        isFirst[EAST][i] = isFirst[SOUTH][i] = isFirst[WEST][i] = isFirst[NORTH][i] = true;
    }

    int multiple = s_NORM_SIZE / s_STRIPESIZE;
    for (int i = 0; i < s_NORM_SIZE; i++) {
        for (int j = 0; j < s_NORM_SIZE; j++) {
            if (*(imageData + s_NORM_SIZE * i + j) == CHAR) {
                if (j > 0 && *(imageData + s_NORM_SIZE * i + j - 1) == BG || j == 0) {
                    transitions[HORIZONTAL]++;

                    strokeDensity[HORIZONTAL][i / multiple]++;

                    if (isFirst[WEST][i]) {
                        isFirst[WEST][i] = false;
                        if (j < peripheral[WEST][i / multiple][FIRST]) {
                            peripheral[WEST][i / multiple][FIRST] = j;
                        }
                    } else { // TODO: ���ǵ��������transition�����
                        if (j < peripheral[WEST][i / multiple][SECOND]) {
                            peripheral[WEST][i / multiple][SECOND] = j;
                        }
                    }
                }

                if (i > 0 && *(imageData + s_NORM_SIZE * (i - 1) + j) == BG || i == 0) {
                    transitions[VERTICAL]++;

                    strokeDensity[VERTICAL][j / multiple]++;

                    if (isFirst[NORTH][j]) {
                        isFirst[NORTH][j] = false;
                        if (i < peripheral[NORTH][j / multiple][FIRST]) {
                            peripheral[NORTH][j / multiple][FIRST] = i;
                        }
                    } else {
                        if (i < peripheral[NORTH][j / multiple][SECOND]) {
                            peripheral[NORTH][j / multiple][SECOND] = i;
                        }
                    }
                }
            }
        }
    }

    for (int i = s_NORM_SIZE - 1; i >= 0; i--) {
        for (int j = s_NORM_SIZE - 1; j >= 0; j--) {
            if (*(imageData + s_NORM_SIZE * i + j) == CHAR) {
                if (j < s_NORM_SIZE - 1 && *(imageData + s_NORM_SIZE * i + j + 1) == BG || j == s_NORM_SIZE - 1) {
                    if (isFirst[EAST][i]) {
                        isFirst[EAST][i] = false;

                        if (s_NORM_SIZE - 1 - j < peripheral[EAST][i / multiple][FIRST]) {
                            peripheral[EAST][i / multiple][FIRST] = s_NORM_SIZE - 1 - j;
                        }
                    } else {
                        if (s_NORM_SIZE - 1 - j < peripheral[EAST][i / multiple][SECOND]) {
                            peripheral[EAST][i / multiple][SECOND] = s_NORM_SIZE - 1 - j;
                        }
                    }
                }

                if (i < s_NORM_SIZE - 1 && *(imageData + s_NORM_SIZE * (i + 1) + j) == BG || i == s_NORM_SIZE - 1) {
                    if (isFirst[SOUTH][j]) {
                        isFirst[SOUTH][j] = false;

                        if (s_NORM_SIZE - 1 - i < peripheral[SOUTH][j / multiple][FIRST]) {
                            peripheral[SOUTH][j / multiple][FIRST] = s_NORM_SIZE - 1 - i;
                        }
                    } else {
                        if (s_NORM_SIZE - 1 - i < peripheral[SOUTH][j / multiple][SECOND]) {
                            peripheral[SOUTH][j / multiple][SECOND] = s_NORM_SIZE - 1 - i;
                        }
                    }
                }
            }
        }
    }
}

void CharFeature::calcLocDirPropAndMaxLocDir(const uchar* imageData, int locDir[][s_GRIDSIZE][4], float strokeProp[][s_GRIDSIZE][4], int maxLocDirCtr[][s_GRIDSIZE][4]) {
    int count = 0, record[s_NORM_SIZE][s_NORM_SIZE][4];

    memset(record, 0, 4 * s_NORM_SIZE * s_NORM_SIZE * sizeof (int));

    for (int i = 0; i < s_NORM_SIZE; i++) {
        for (int j = 0; j < s_NORM_SIZE; j++) {
            if (*(imageData + s_NORM_SIZE * i + j) == CHAR) {
                if (record[i][j][LEFT_RIGHT] == 0) {
                    count = 1;

                    while (j + count < s_NORM_SIZE && *(imageData + s_NORM_SIZE * i + j + count) == CHAR) {
                        count++;
                    }

                    for (int k = 0; k < count; k++) {
                        record[i][j + k][LEFT_RIGHT] = count;
                    }
                }

                if (record[i][j][UP_DOWN] == 0) {
                    count = 1;

                    while (i + count < s_NORM_SIZE && *(imageData + s_NORM_SIZE * (i + count) + j) == CHAR) {
                        count++;
                    }

                    for (int k = 0; k < count; k++) {
                        record[i + k][j][UP_DOWN] = count;
                    }
                }

                if (record[i][j][LUP_RDOWN] == 0) {
                    count = 1;

                    while (j + count < s_NORM_SIZE && i + count < s_NORM_SIZE && *(imageData + s_NORM_SIZE * (i + count) + j + count) == CHAR) {
                        count++;
                    }

                    for (int k = 0; k < count; k++) {
                        record[i + k][j + k][LUP_RDOWN] = count;
                    }
                }

                if (record[i][j][LDOWN_RUP] == 0) {
                    count = 1;

                    while (i + count < s_NORM_SIZE && j >= count && *(imageData + s_NORM_SIZE * (i + count) + j - count) == CHAR) {
                        count++;
                    }

                    for (int k = 0; k < count; k++) {
                        record[i + k][j - k][LDOWN_RUP] = count;
                    }
                }
            }
        }
    }

    int total[s_GRIDSIZE][s_GRIDSIZE][4], pixs[s_GRIDSIZE][s_GRIDSIZE];
    int multiple = s_NORM_SIZE / s_GRIDSIZE;

    memset(total, 0, 4 * s_GRIDSIZE * s_GRIDSIZE * sizeof (int));
    memset(maxLocDirCtr, 0, 4 * s_GRIDSIZE * s_GRIDSIZE * sizeof (int));
    memset(pixs, 0, s_GRIDSIZE * s_GRIDSIZE * sizeof (int));

    bool tag;
    for (int i = 0; i < s_NORM_SIZE; i++) {
        for (int j = 0; j < s_NORM_SIZE; j++) {
            tag = false;

            if (record[i][j][UP_DOWN] > 0) {
                tag = true;

                total[i / multiple][j / multiple][UP_DOWN] += record[i][j][UP_DOWN];
            }

            if (record[i][j][LEFT_RIGHT] > 0) {
                tag = true;

                total[i / multiple][j / multiple][LEFT_RIGHT] += record[i][j][LEFT_RIGHT];
            }

            if (record[i][j][LUP_RDOWN] > 0) {
                tag = true;

                total[i / multiple][j / multiple][LUP_RDOWN] += record[i][j][LUP_RDOWN];
            }

            if (record[i][j][LDOWN_RUP] > 0) {
                tag = true;

                total[i / multiple][j / multiple][LDOWN_RUP] += record[i][j][LDOWN_RUP];
            }

            if (tag) {
                pixs[i / multiple][j / multiple]++;

                int m_max = 0, offset = 0;
                for (int k = 0; k < 4; k++) {
                    if (m_max < record[i][j][k]) {
                        m_max = record[i][j][k];

                        offset = k;
                    }
                }
                maxLocDirCtr[i / multiple][j / multiple][offset]++;
            }
        }
    }

    memset(strokeProp, 0, 2 * s_GRIDSIZE * 4 * sizeof (float));

    int temp;
    for (int i = 0; i < s_GRIDSIZE; i++) {
        for (int j = 0; j < s_GRIDSIZE; j++) {
            temp = pixs[i][j];

            locDir[i][j][LEFT_RIGHT] = (temp == 0) ? 0 : total[i][j][LEFT_RIGHT] / temp;
            locDir[i][j][UP_DOWN] = (temp == 0) ? 0 : total[i][j][UP_DOWN] / temp;
            locDir[i][j][LUP_RDOWN] = (temp == 0) ? 0 : total[i][j][LUP_RDOWN] / temp;
            locDir[i][j][LDOWN_RUP] = (temp == 0) ? 0 : total[i][j][LDOWN_RUP] / temp;

            temp = maxLocDirCtr[i][j][LEFT_RIGHT];
            strokeProp[HORIZONTAL][i][LEFT_RIGHT] += temp;
            strokeProp[VERTICAL][j][LEFT_RIGHT] += temp;

            temp = maxLocDirCtr[i][j][UP_DOWN];
            strokeProp[HORIZONTAL][i][UP_DOWN] += temp;
            strokeProp[VERTICAL][j][UP_DOWN] += temp;

            temp = maxLocDirCtr[i][j][LUP_RDOWN];
            strokeProp[HORIZONTAL][i][LUP_RDOWN] += temp;
            strokeProp[VERTICAL][j][LUP_RDOWN] += temp;

            temp = maxLocDirCtr[i][j][LDOWN_RUP];
            strokeProp[HORIZONTAL][i][LDOWN_RUP] += temp;
            strokeProp[VERTICAL][j][LDOWN_RUP] += temp;
        }
    }

    for (int i = 0; i < s_GRIDSIZE; i++) {
        temp = pixs[i][0] + pixs[i][1] + pixs[i][2] + pixs[i][3];
        strokeProp[HORIZONTAL][i][LEFT_RIGHT] = (temp == 0) ? 0 : strokeProp[HORIZONTAL][i][LEFT_RIGHT] / temp;
        strokeProp[HORIZONTAL][i][UP_DOWN] = (temp == 0) ? 0 : strokeProp[HORIZONTAL][i][UP_DOWN] / temp;
        strokeProp[HORIZONTAL][i][LUP_RDOWN] = (temp == 0) ? 0 : strokeProp[HORIZONTAL][i][LUP_RDOWN] / temp;
        strokeProp[HORIZONTAL][i][LDOWN_RUP] = (temp == 0) ? 0 : strokeProp[HORIZONTAL][i][LDOWN_RUP] / temp;

        temp = pixs[0][i] + pixs[1][i] + pixs[2][i] + pixs[3][i];
        strokeProp[VERTICAL][i][LEFT_RIGHT] = (temp == 0) ? 0 : strokeProp[VERTICAL][i][LEFT_RIGHT] / temp;
        strokeProp[VERTICAL][i][UP_DOWN] = (temp == 0) ? 0 : strokeProp[VERTICAL][i][UP_DOWN] / temp;
        strokeProp[VERTICAL][i][LUP_RDOWN] = (temp == 0) ? 0 : strokeProp[VERTICAL][i][LUP_RDOWN] / temp;
        strokeProp[VERTICAL][i][LDOWN_RUP] = (temp == 0) ? 0 : strokeProp[VERTICAL][i][LDOWN_RUP] / temp;
    }
}

void CharFeature::calcBlackJump(const uchar* imageData, int totalBlackJump[][s_SUBVCOUNT], float divBlackJump[][s_SUBVCOUNT]) {
    uchar slantingData[s_NORM_SIZE * s_NORM_SIZE];
    int sum, temp;

    for (int i = 0; i < s_NORM_SIZE; i++) {
        for (int j = 0; j < s_NORM_SIZE; j++) {
            temp = i + j;
            if (temp < s_NORM_SIZE) {
                sum = temp * (temp + 1) / 2;
                slantingData[sum + j] = *(imageData + s_NORM_SIZE * i + j);
            } else {
                temp = 2 * s_NORM_SIZE - 2 - temp;

                sum = temp * (temp + 1) / 2;
                slantingData[s_NORM_SIZE * s_NORM_SIZE - sum - s_NORM_SIZE + j] = *(imageData + s_NORM_SIZE * i + j); // s_NORM_SIZE*s_NORM_SIZE - 1 - sum - (s_NORM_SIZE - 1 - j)
            }
        }
    }

    memset(totalBlackJump, 0, 2 * s_SUBVCOUNT * sizeof (int));

    int totalH = 0, totalS = 0, interval = s_NORM_SIZE * s_NORM_SIZE / s_SUBVCOUNT;
    for (int i = 0; i < s_SUBVCOUNT; i++) {
        for (int j = 0; j < interval; j++) {
            if (*(imageData + interval * i + j) == CHAR && (i == 0 && j == 0 || *(imageData + interval * i + j - 1) == BG)) {
                totalBlackJump[HORIZONTAL][i]++;

                totalH++;
            }

            if (*(slantingData + interval * i + j) == CHAR && (i == 0 && j == 0 || *(slantingData + interval * i + j - 1) == BG)) {
                totalBlackJump[SLANTING][i]++;

                totalS++;
            }
        }
    }

    for (int i = 0; i < s_SUBVCOUNT; i++) {
        divBlackJump[HORIZONTAL][i] = totalBlackJump[HORIZONTAL][i]*1.0 / totalH;

        divBlackJump[SLANTING][i] = totalBlackJump[SLANTING][i]*1.0 / totalS;
    }
}

void CharFeature::saveData() {
    // for feature.mm has been stable, no need to update it now
    throw "for feature.mm has been stable, no need to update it now";
    
    /*ofstream fout(Config::getInstance().getString("path.feature_mm").c_str());

    for (int i = 0; i < s_FEATURE_SIZE; i++) {
        fout << m_max[i] << " " << m_min[i] << endl;
    }

    fout.close();*/
}
